package decoder

import (
	"strconv"
	"encoding/hex"
	"math"
)

type DicomReader struct {
	byteReader   *ByteReader
	stringReader *StringReader
}

func newDicomReader() *DicomReader {
	byteReader := &ByteReader{}
	stringReader := &StringReader{}
	return &DicomReader{byteReader: byteReader, stringReader: stringReader}
}

func (this *DicomReader) Init(code []byte, pc int) {
	this.byteReader.reset(code, pc)
}

func (this *DicomReader) Finished() bool {
	return this.byteReader.finished()
}

func (this *DicomReader) readHeader() string {
	var str string
	for i := 0; i < 4; i++ {
		s := this.byteReader.readString()
		str += s
	}
	return str
}

func (this *DicomReader) readTag() []string {
	bytes0 := this.byteReader.readByte()
	bytes1 := this.byteReader.readByte()
	bytes2 := this.byteReader.readByte()
	bytes3 := this.byteReader.readByte()
	tag0 := hex.EncodeToString([]byte{bytes1, bytes0})
	tag1 := hex.EncodeToString([]byte{bytes3, bytes2})
	return []string{tag0, tag1}
}

func (this *DicomReader) readVRAndVLAndValue(tag []string, charset string, isExplicitVR, isPixel, isLittleEndian, isSigned bool, isDir int) (vr string, vl int, vf string, data []byte) {
	vr, vl = this.readVRAndVL(tag, isExplicitVR)

	if tag[0] == "7fe0" && tag[1] == "0010" {
		isPixel = true
		if vl == math.MaxUint32 {
			isDir = 1
		} else {
			vf, data = this.readValue(vr, charset, vl, isPixel, isLittleEndian, isSigned)
		}
	} else if (vr == "SQ" && vl == math.MaxUint32) || (tag[0] == "fffe" && tag[1] == "e000" && vl == math.MaxUint32) {
		isDir = 1
	} else if tag[0] == "fffe" && tag[1] == "e00d" && vl == 0 {
		isDir = 1
	} else if tag[0] == "fffe" && tag[1] == "e0dd" && vl == 0 {
		isDir = 2
		if isPixel {
			isPixel = false
		}
	} else {
		vf, data = this.readValue(vr, charset, vl, isPixel, isLittleEndian, isSigned)
	}
	return vr, vl, vf, data
}

func (this *DicomReader) readVRAndVL(tag []string, isExplicitVR bool) (vr string, vl int) {
	if tag[0] == "fffe" && ( tag[1] == "e000" || tag[1] == "e00d" || tag[1] == "e0dd") {
		vr = "**"
		vl = int(this.byteReader.readUint32())
	} else if tag[0] == "0002" || isExplicitVR {
		vr = this.byteReader.readMultiString(2)
		if vr == "OB" || vr == "OW" || vr == "SQ" || vr == "OF" || vr == "UT" || vr == "UN" {
			this.skip(2)
			vl = int(this.byteReader.readUint32())
		} else {
			vl = int(this.byteReader.readUint16())
		}
	} else if !isExplicitVR {
		vr = getVR(tag)
		vl = int(this.byteReader.readUint32())
	}
	return vr, vl
}

func (this *DicomReader) readValue(vr, charset string, vl int, isPixel, isLittleEndian, isSigned bool) (vf string, data []byte) {
	var value string
	switch vr {
	case "AE", "AT", "UI", "SH", "DA", "DT", "TM", "CS", "LO", "LT", "ST", "UC", "UR", "UT", "AS", "DS":
		value += this.byteReader.readMultiString(vl)
	case "PN":
		// read string
		v := this.byteReader.readMultiString(vl)
		value += this.stringReader.GetString(v, charset)
	case "FL":
		// read float
		value += strconv.FormatFloat(float64(this.byteReader.readMultiFloat(vl)), 'f', -1, 64)
	case "FD":
		// read double
		value += strconv.FormatFloat(this.byteReader.readMultiDouble(vl), 'f', -1, 64)
	case "IS", "SL", "SS", "UL":
		// read int
		for i := 0; i < vl/2; i++ {
			v := int(this.byteReader.readUint16())
			v1, _ := strconv.ParseInt(value, 10, 10)
			v += int(v1)
			value = strconv.Itoa(int(v))
		}
	case "US":
		if isPixel {
			data = this.byteReader.readBytes(vl)
		} else {
			for i := 0; i < vl/2; i++ {
				v := int(this.byteReader.readUint16())
				v1, _ := strconv.ParseInt(value, 10, 10)
				v += int(v1)
				value = strconv.Itoa(int(v))
			}
		}
	case "SQ":
		// read sequence
		value += this.byteReader.readMultiString(vl)
	case "OD", "OF", "OL", "UN":
		// read bulk data
		value += this.byteReader.readMultiString(vl)
	case "OB", "OW":
		if isPixel {
			data = this.byteReader.readBytes(vl)
		} else {
			value += this.byteReader.readHexString(vl)
		}
	default:
		if isPixel {
			data = this.byteReader.readBytes(vl)
		} else {
			this.skip(vl)
		}
	}
	return value, data
}

func (this *DicomReader) skip(vl int) {
	this.byteReader.skip(vl)
}
